#include "Celko.h"

bool Celko::IsVowel (BYTE c) {
	return (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' || c == 'Y');
}

void Celko::CleanUp (BYTE *name, ULONG &length) {
	ULONG read = 0;
	ULONG write = 0;
	BYTE firstchar = '-';

	// Eliminate invalid characters and uppercase all
	// Replace all vowels with 'A'

	for (ULONG i = 0; i < length; i++) {
		BYTE c = *(name + read++);
		if (c >= 'a' && c <= 'z')
			c = c - 'a' + 'A';
		if (c >= 'A' && c <= 'Z') {
			if (firstchar == '-')
				firstchar = c;
			if (IsVowel(c))
				*(name + write++) = 'A';
			else
				*(name + write++) = c;
		}
	}
	length = write;
	while (write < read) {
		*(name + write++) = ' ';
	}

	// Put back the first character, this is in case the first char is a vowel
	// it will be converted back from A

	if (firstchar != '-')
		*(name) = firstchar;
}



	
const char *Prefixes[] = {"MAC", "PH", "PF", "KN", "K", "SCH"};
const char *Prefix_Replace[] = {"MCC", "FF", "FF", "NN", "C", "SSS"};
const char *NGrams[] = {"DG", "CAAN", "D", "NST", "AV", "Q", "Z", "M", "LN", "K", "AHA", "H", "AW", "PH", "SCH"};
const char *NGram_Replace[] = {"GG", "TAAN", "T", "NSS", "AF", "G", "S", "N", "NN", "C", "AHA", "A", "AA", "FF", "SSS"};

bool Celko::StartsWith (BYTE *name, ULONG length, const char *prefix) {
	bool f = true;
	for (ULONG i = 0; i < length && *(prefix + i) != '\0'; i++) {
		if (*(name + i) != *(prefix + i))
			f = false;
	}
	if (*(prefix + i) != '\0') {
		f = false;
	}
	return f;
}

void Celko::ReplacePrefix (BYTE *name, const char *prefix, ULONG &start) {
	for (ULONG i = 0; *(prefix + i) != '\0'; i++) {
		*(name + i) = *(prefix + i);
		start = i;
	}
}

bool Celko::CheckNGram (BYTE *name, ULONG &start, ULONG length, const char *ngram) {
	bool f = true;
	for (ULONG i = start; i < length && *(ngram + i - start) != '\0'; i++) {
		if (*(name + i) != *(ngram + i - start))
			f = false;
	}
	if (*(ngram + i - start) != '\0') {
		f = false;
	}
	return f;
}

void Celko::ReplaceNGram (BYTE *name, const char *ngram, ULONG &start) {
	ULONG j = 0;
	for (ULONG i = start; *(ngram + i - start) != '\0'; i++) {
		*(name + i) = *(ngram + i - start);
		j = i;
	}
	start = j;
}

void Celko::Summarize(BYTE *name, ULONG &length) {
	// This is the post-processing loop that eliminates vowels and summarizes
	// duplicate consonants

	ULONG read = 1;
	ULONG write = 1;
	BYTE lastc = '-';
	BYTE currentc = '-';

    while (read < length) {
		BYTE lastc = *(name + read - 1);
		BYTE currentc = *(name + read);

		if (lastc != currentc) {
			if (currentc != 'A')
				*(name + write++) = currentc;
		}
		read++;
	}
	length = write;
	while (write < read)
		*(name + write++) = '\0';
}

void Celko::Encode (BYTE *name, ULONG length) {
	CleanUp (name, length);
	ULONG i = 0;
	ULONG start = 0;
	bool done = false;
	while (i < 6 && !done) {
		if (StartsWith(name, length, Prefixes[i])) {
			ReplacePrefix(name, Prefix_Replace[i], start);
			done = true;
		}
		i++;
	}

	// This is the main loop that reduces our name down to its phonetic
	// equivalent

	while (start < length) {
		i = 0;
		done = false;
		while (i < 15 && !done) {
			if (CheckNGram(name, start, length, NGrams[i])) {
				ReplaceNGram(name, NGram_Replace[i], start);
				done = true;
			}
			i++;
		}
		if (!done) {
			start++;
		}
	}

	// Drop terminal 'A' and 'S' characters

	ULONG finish = start;
	while (finish > 1 && *(name + finish - 1) == 'S')
		finish--;
	while (finish > 1 && *(name + finish - 1) == 'A')
		finish--;

	// Convert Terminal "NT" to "TT"

	if (finish >= 1 && *(name + finish - 2) == 'N' && *(name + finish - 1) == 'T')
		*(name + finish - 2) = 'T';

	for (i = finish; i < length; i++)
		*(name + i) = ' ';

	Summarize(name, length);
	memset(Result, ' ', 4);
	*(Result + 4) = '\0';
	length = (finish >= 4) ? 4 : finish;
	memcpy(Result, name, length);
}
